/* This is a customised template.
 * It overrides some optional methods from EntityMap to allow for testing 
 * with Mocks and fast object creation. Search for #custom# below. 
 */

#set($entity = $Neo.Entity)

#if($Neo.Entity.Namespace)
namespace $Neo.Entity.Namespace
{
#end
using System;
using System.Collections;
using System.Data;
using Neo.Core;
#if($entity.Relationships.Count > 0)
using Neo.Core.Util;
#end
using Neo.Framework;
#foreach($ns in $entity.UsedNamespaces)
#if($ns != $entity.Namespace)
using $ns;
#end
#end

//-------- Base Class ----------------------------------------------------

#if(${entity.BaseClassName})
public class ${entity.ClassName}Base : ${entity.BaseClassName}
#else
public class ${entity.ClassName}Base : EntityObject
#end
{
#foreach($rel in $entity.ToManyRelationships)
	public readonly ${rel.ForeignEntity.ClassName}Relation ${rel.DotNetName};
#end
       
	protected ${entity.ClassName}Base(System.Data.DataRow aRow, Neo.Core.ObjectContext aContext) : base(aRow, aContext)
	{
#foreach($rel in $entity.ToManyRelationships)
		$rel.DotNetName = new ${rel.ForeignEntity.ClassName}Relation(this, "$rel.DotNetName");
#end
	}
	
#foreach($attribute in $entity.Attributes)
#if(!$attribute.IsHidden)
	public virtual $attribute.DotNetType $attribute.DotNetName
	{
#if($attribute.DotNetType == "System.String")
		get { return Row["$attribute.ColumnName"] as System.String; }
		set { Row["$attribute.ColumnName"] = (value != null) ? (object)value : (object)DBNull.Value; }
#else
		get { object v = Row["$attribute.ColumnName"]; return ($attribute.DotNetType)((v != DBNull.Value) ? v : HandleNullValueForProperty("$attribute.DotNetName")); }
		set { Row["$attribute.ColumnName"] = value; }
#end
	}    

#end
#end

#foreach($rel in $entity.ToOneRelationships)
	public virtual $rel.ForeignEntity.ClassName $rel.DotNetName
	{
		get { object fk = Row["$rel.LocalKey"]; return (fk == DBNull.Value) ? null : ($rel.ForeignEntity.ClassName)GetRelatedObject("$rel.ForeignEntity.TableName", fk); }
		set { SetRelatedObject(value, "$rel.LocalKey", "$rel.ForeignKey" ); }
	}

#end

	public override object GetProperty(string propName)
	{
#foreach($attribute in $entity.Attributes)
#if(!$attribute.IsHidden)
		if(propName == "$attribute.DotNetName") 
			return $attribute.DotNetName;
#end
#end
#foreach($rel in $entity.ToOneRelationships)
		if(propName == "$rel.DotNetName") 
			return $rel.DotNetName;
#end

		return base.GetProperty(propName);
	}

}


//-------- Query Template ------------------------------------------------

public class ${entity.ClassName}Template : QueryTemplate
{
	public ${entity.ClassName}Template(IEntityMap anEntityMap) : base(anEntityMap)
	{
	}
	
#foreach($attribute in $entity.Attributes)
#if(!$attribute.IsHidden)
	public $attribute.DotNetType $attribute.DotNetName
	{
		get { return ($attribute.DotNetType)QueryValues["$attribute.DotNetName"]; }
		set { QueryValues["$attribute.DotNetName"] = value; }
	}

#end
#end
#foreach($rel in $entity.ToOneRelationships)
	public $rel.ForeignEntity.ClassName $rel.DotNetName
	{
		get { return ($rel.ForeignEntity.ClassName)QueryValues["$rel.DotNetName"]; }
		set { QueryValues["$rel.DotNetName"] = value; }
	}

#end
               
}


//-------- Typed Collections ----------------------------------------------

public class ${entity.ClassName}List : ObjectListBase
{
	public ${entity.ClassName}List()
	{
	}

	public ${entity.ClassName}List(IList list)
	{
		((ArrayList)InnerList).AddRange(list);
	}

	public ${entity.ClassName} this[int index]
	{
		get { return (${entity.ClassName})InnerList[index]; }
	}

	public int Add(${entity.ClassName} new${entity.ClassName})
	{
		return base.Add(new${entity.ClassName});
	}

	public void Remove(${entity.ClassName} existing${entity.ClassName})
	{
		base.Remove(existing${entity.ClassName});
	}

	public bool Contains(${entity.ClassName} existing${entity.ClassName})
	{
		return base.Contains(existing${entity.ClassName});
	}

	public int IndexOf(${entity.ClassName} existing${entity.ClassName})
	{
		return base.IndexOf(existing${entity.ClassName});
	}
	
	public ${entity.ClassName}List Find(string qualifierFormat, params object[] parameters)
	{
		${entity.ClassName}List resultSet = new ${entity.ClassName}List();
		base.Find(resultSet, qualifierFormat, parameters);
		return resultSet;
	}

	public new ${entity.ClassName} FindUnique(string qualifierFormat, params object[] parameters)
	{
		return (${entity.ClassName})base.FindUnique(qualifierFormat, parameters);
	}
	
	public new ${entity.ClassName} FindFirst(string qualifierFormat, params object[] parameters)
	{
		return (${entity.ClassName})base.FindFirst(qualifierFormat, parameters);
	}
	
}


public class ${entity.ClassName}Relation : ObjectRelationBase
{
	public ${entity.ClassName}Relation(IEntityObject eo, string aRelation) : base(eo, aRelation)
	{
	}

	public ${entity.ClassName} this[int index]
	{
		get { return (${entity.ClassName})InnerList[index]; }
	}

	public int Add(${entity.ClassName} new${entity.ClassName})
	{
		return base.Add(new${entity.ClassName});
	}

	public void Remove(${entity.ClassName} existing${entity.ClassName})
	{
		base.Remove(existing${entity.ClassName});
	}

	public bool Contains(${entity.ClassName} existing${entity.ClassName})
	{
		return base.Contains(existing${entity.ClassName});
	}

	public int IndexOf(${entity.ClassName} existing${entity.ClassName})
	{
		return base.IndexOf(existing${entity.ClassName});
	}

	public ${entity.ClassName}List GetReadOnlyList()
	{
		${entity.ClassName}List resultSet = new ${entity.ClassName}List();
		base.CopyToListAndMakeReadOnly(resultSet);
		return resultSet;
	}
	
	public ${entity.ClassName}List GetSortedList(string propName, SortDirection dir)
	{
		${entity.ClassName}List resultSet = new ${entity.ClassName}List();
		base.CopyToListAndSort(resultSet, propName, dir);
		return resultSet;
	}

	public ${entity.ClassName}List Find(string qualifierFormat, params object[] parameters)
	{
		${entity.ClassName}List resultSet = new ${entity.ClassName}List();
		base.Find(resultSet, qualifierFormat, parameters);
		return resultSet;
	}

	public new ${entity.ClassName} FindUnique(string qualifierFormat, params object[] parameters)
	{
		return (${entity.ClassName})base.FindUnique(qualifierFormat, parameters);
	}
	
	public new ${entity.ClassName} FindFirst(string qualifierFormat, params object[] parameters)
	{
		return (${entity.ClassName})base.FindFirst(qualifierFormat, parameters);
	}

}


//-------- Object Factory ------------------------------------------------

public class ${entity.ClassName}Factory : Neo.Framework.ObjectFactory
{
	public ${entity.ClassName}Factory(ObjectContext context) : base(context, typeof($entity.ClassName))
	{
	}

#if($entity.IdMethod.ToString == "None")
#if($entity.PrimaryKeyIsForeignKey)
#foreach($combination in $entity.RelationshipSetsForColumns($entity.PkColumns))
	public $entity.ClassName CreateObject(#foreach($rel in $combination)#set( $pnum = $velocityCount - 1)#if( $pnum > 0), #end$rel.ForeignEntity.ClassName arg${pnum}#end)
	{
		return ($entity.ClassName)base.CreateObject(new object[] {#foreach($rel in $combination)#set( $pnum = $velocityCount - 1)#if( $pnum > 0),#end arg${pnum}.Row["$rel.ForeignKey"]#end } );
	}

#end
#else
	public $entity.ClassName CreateObject(#foreach($column in $entity.PkColumns)#set( $pnum = $velocityCount - 1)#if( $pnum > 0), #end#if( $entity.ColumnIsForeignKey($column.ColumnName))$entity.RelationshipForAttribute($column).ForeignEntity.ClassName#else$column.DotNetType#end arg${pnum}#end)
	{
		return ($entity.ClassName)base.CreateObject(new object[] {#foreach($column in $entity.PkColumns)#set( $pnum = $velocityCount - 1)#if( $entity.ColumnIsForeignKey($column.ColumnName)) arg${pnum}.Row["$entity.RelationshipForAttribute($column).ForeignKey"]#else arg${pnum}#end#if( $velocityCount < $entity.PkColumns.Count),#end#end } );
	}
	
#end
#else	 
	public $entity.ClassName CreateObject()
	{
		return ($entity.ClassName)base.CreateObject(null);
	}
	
#end 
	public $entity.ClassName FindObject(#foreach($column in $entity.PkColumns)#set( $pnum = $velocityCount - 1)#if( $pnum > 0), #end#if( $entity.ColumnIsForeignKey($column.ColumnName))$entity.RelationshipForAttribute($column).ForeignEntity.ClassName#else$column.DotNetType#end arg${pnum}#end)
	{
		return ($entity.ClassName)base.FindObject(new object[] {#foreach($column in $entity.PkColumns)#set( $pnum = $velocityCount - 1)#if( $entity.ColumnIsForeignKey($column.ColumnName)) arg${pnum}.Row["$entity.RelationshipForAttribute($column).ForeignKey"]#else arg${pnum}#end#if( $velocityCount < $entity.PkColumns.Count),#end#end } );
	}

	public new ${entity.ClassName}List FindAllObjects()
	{
		${entity.ClassName}List c = new ${entity.ClassName}List();
		foreach(${entity.ClassName} eo in base.FindAllObjects())
			c.Add(eo);
		return c;
	}
	
	public ${entity.ClassName}Template GetQueryTemplate()
	{
		return new ${entity.ClassName}Template(EntityMap);
	}
	
	public ${entity.ClassName}List Find(${entity.ClassName}Template template)
	{
		${entity.ClassName}List c = new ${entity.ClassName}List();
		foreach(${entity.ClassName} eo in base.Find(template))
			c.Add(eo);
		return c;
	}

	public ${entity.ClassName}List Find(FetchSpecification fetchSpecification)
	{
		${entity.ClassName}List c = new ${entity.ClassName}List();
		foreach(${entity.ClassName} eo in base.Find(fetchSpecification))
			c.Add(eo);
		return c;
	}
	
	public new ${entity.ClassName}List Find(Qualifier qualifier)
	{
		${entity.ClassName}List c = new ${entity.ClassName}List();
		foreach(${entity.ClassName} eo in base.Find(qualifier))
			c.Add(eo);
		return c;
	}
	
	public new ${entity.ClassName}List Find(string qualifierFormat, params object[] parameters)
	{
		${entity.ClassName}List c = new ${entity.ClassName}List();
		foreach(${entity.ClassName} eo in base.Find(qualifierFormat, parameters))
			c.Add(eo);
		return c;
	}

	public new ${entity.ClassName}List FindWithLimit(int limit, string qualifierFormat, params object[] parameters)
	{
		${entity.ClassName}List c = new ${entity.ClassName}List();
		foreach(${entity.ClassName} eo in base.Find(qualifierFormat, parameters))
			c.Add(eo);
		return c;
	}
	
	public new ${entity.ClassName} FindFirst(string qualifierFormat, params object[] parameters)
	{
		return (${entity.ClassName})base.FindFirst(qualifierFormat, parameters);
	}
	
	public new ${entity.ClassName} FindFirst(Qualifier q)
	{
		return (${entity.ClassName})base.FindFirst(q);
	}
	
	public new ${entity.ClassName} FindUnique(string qualifierFormat, params object[] parameters)
	{
		return (${entity.ClassName})base.FindUnique(qualifierFormat, parameters);
	}	

	
}


//-------- Entity Map -----------------------------------------------------

internal class ${entity.ClassName}Map : EntityMap
{
    private static readonly string[] pkcolumns = {#foreach($column in $entity.PkColumns)#if( $velocityCount > 1),#end "$column.ColumnName"#end };
    private static readonly string[] columns = {#foreach($column in $entity.Attributes)#if(!$attribute.IsHidden)#if( $velocityCount > 1),#end "$column.ColumnName"#end#end };
    private static readonly string[] attributes = {#foreach($column in $entity.Attributes)#if(!$attribute.IsHidden)#if( $velocityCount > 1),#end "$column.DotNetName"#end#end };
    private static readonly string[] relations = {#foreach($rel in $entity.Relationships)#if( $velocityCount > 1),#end "$rel.DotNetName"#end };
    
    private Type concreteObjectType = typeof($entity.ClassName);				/* #custom# added */

    public override System.Type ObjectType
    {
        get { return typeof($entity.ClassName); }
    }
    
    public override System.Type ConcreteObjectType
    {
        get { return concreteObjectType; }										 /* #custom# overridden */
        set { concreteObjectType = value; Factory.AddCustomType(value, this); }  /* #custom# overridden */
    }
    
    public override string TableName
    {
        get { return "${entity.TableName}"; }
    }
    
    public override string[] PrimaryKeyColumns
    {
        get { return pkcolumns; }
    }

    public override string[] Columns
    {
        get { return columns; }
    }

    public override string[] Attributes
    {
        get { return attributes; }
    }

    public override string[] Relations
    {
        get { return relations; }
    }

  	protected override IDictionary GetRelationInfos()
    {
    	IDictionary infos = new Hashtable($entity.Relationships.Count);
 #foreach($rel in $entity.Relationships)
 #if($rel.Direction.ToString == "Parent")
		infos.Add("$rel.DotNetName", new RelationInfo(Factory, typeof($rel.LocalEntity.ClassName), typeof($rel.ForeignEntity.ClassName), "$rel.LocalKey", "$rel.ForeignKey"));
#else
		infos.Add("$rel.DotNetName", new RelationInfo(Factory, typeof($rel.ForeignEntity.ClassName), typeof($rel.LocalEntity.ClassName), "$rel.ForeignKey", "$rel.LocalKey"));
#end
#end
		return infos;    	
    }
    
	public override IPkInitializer GetPkInitializer()
	{
#if($entity.IdMethod.ToString == "None")
		return new UserPkInitializer();
#elseif($entity.IdMethod.ToString == "Native")
		return new NativePkInitializer();
#elseif($entity.IdMethod.ToString == "IdBroker")
		return new IdBrokerPkInitializer();
#elseif($entity.IdMethod.ToString == "Guid")
		return new UuidType4PkInitializer();
#end
	}
	
	public override IEntityObject CreateInstance(DataRow row, ObjectContext context) /* #custom# overridden */
	{
		if(ConcreteObjectType != ObjectType)
			return base.CreateInstance(row, context);
		return new ${entity.ClassName}(row, context);
	}
    
     protected override void WriteBasicSchema(DataTable table)
    {
		DataColumn		c;
		
#foreach($attribute in $entity.Attributes)
		c = table.Columns.Add("$attribute.ColumnName", typeof($attribute.DotNetType));
#if($attribute.IsPkColumn)
#if($entity.PkColumns.Count == 1) 
		c.Unique = true;
#end
#if(($entity.IdMethod.ToString == "Native") || ($entity.IdMethod.ToString == "IdBroker"))
		c.AutoIncrement = true; c.AutoIncrementSeed = c.AutoIncrementStep = -1;
#end
#end
#if($attribute.AllowsNull)
		c.AllowDBNull = true;
#end
#end
		table.PrimaryKey = new DataColumn[] {#foreach($column in $entity.PkColumns)#if( $velocityCount > 1),#end table.Columns["$column.ColumnName"]#end };
	}
	
#if($entity.Relationships.Count > 0)
	protected override void WriteRelations(DataTable table)
	{
		DataRelation r;
		
#foreach($rel in $entity.Relationships)
#if($rel.Direction.ToString == "Parent") ## yes, that's how you compare enums...
		if(table.DataSet.Relations["${rel.LocalEntity.TableName}*${rel.ForeignEntity.TableName}.${rel.ForeignKey}"] == null)
		{
			r = table.DataSet.Relations.Add("${rel.LocalEntity.TableName}*${rel.ForeignEntity.TableName}.${rel.ForeignKey}", 
					table.DataSet.Tables["$rel.LocalEntity.TableName"].Columns["$rel.LocalKey"],
					table.DataSet.Tables["$rel.ForeignEntity.TableName"].Columns["$rel.ForeignKey"]);
			r.ChildKeyConstraint.UpdateRule = $rel.DotNetUpdateRule;
			r.ChildKeyConstraint.DeleteRule = $rel.DotNetDeleteRule;
		}
#else
		if(table.DataSet.Relations["${rel.ForeignEntity.TableName}*${rel.LocalEntity.TableName}.${rel.LocalKey}"] == null)
		{
			r = table.DataSet.Relations.Add("${rel.ForeignEntity.TableName}*${rel.LocalEntity.TableName}.${rel.LocalKey}", 
					table.DataSet.Tables["$rel.ForeignEntity.TableName"].Columns["$rel.ForeignKey"],
					table.DataSet.Tables["$rel.LocalEntity.TableName"].Columns["$rel.LocalKey"]);
#set($rel = $rel.InverseRelationship)
#if($rel)
			r.ChildKeyConstraint.UpdateRule = $rel.DotNetUpdateRule;
			r.ChildKeyConstraint.DeleteRule = $rel.DotNetDeleteRule;
#else
			r.ChildKeyConstraint.UpdateRule = Rule.Cascade;
			r.ChildKeyConstraint.DeleteRule = Rule.SetNull; /* child and can't find inverse */
#end
		}
#end			
#end
	}
#end
}

#if($Neo.Entity.Namespace)
}
#end

